import json
import os.path

from django.conf import settings
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.renderers import JSONRenderer

from sqtp.models import Request, Case, Config, Step, Project
from sqtp.serializers import UserSerializer
from sqtp.serializers.mgr import ProjectSerializer, EnvironmentSerializer
from sqtp.utils.utils import Utils

'''
要想调用一个case/1，返回其他的如request的字段，请定义相关字段=相关字段序列化器
'''


class RequestSerializer(serializers.ModelSerializer):
    # read_only=True 仅用来读，写入时用原方式
    method = serializers.SerializerMethodField(read_only=True)  # 必须找到一个 get_method(self, obj)，否则直接报错
    step_id = serializers.IntegerField(write_only=True, required=False)
    # project = ProjectSerializer(read_only=True)
    # envir = EnvironmentSerializer(read_only=True)

    class Meta:
        model = Request
        '''
        会自动包含我上面写的method哪些字段
        '''
        fields = '__all__'

    # step = StepSerializer(read_only=True)
    # step_id = serializers.PrimaryKeyRelatedField( 不管多对多，一对多，一对一都可,但多对多要自己写create，具体请问ai
    #     queryset=Step.objects.all(), write_only=True,
    #     many= 告诉序列化器这个字段接收的是一个对象还是多个对象
    #     source='step' #映照model中定义的step字段
    # )
    '''
    多对多
    class StepSerializer(serializers.ModelSerializer):
    tag_ids = serializers.PrimaryKeyRelatedField(
        queryset=Tag.objects.all(),
        many=True,          # 多个 ID
        source='tags'       # 映射到 tags 多对多字段
    )

    class Meta:
        model = Step
        fields = ['tag_ids']

    def create(self, validated_data):
        tags = validated_data.pop('tags', [])
        step = Step.objects.create(**validated_data)
        step.tags.set(tags)  # 多对多必须手动 set/add
        return step
    '''
    '''
    validate 方法不会覆盖内置字段的校验，反而是在内置校验基础上做补充和强化。
    '''

    def validate(self, attrs):
        template = {
            "params": dict,
            "headers": dict,
            "cookies": dict,
        }
        Utils.validate(attrs, template)
        return attrs

    def get_method(self, obj):  # rest框架获取method时自动调用该方法
        return obj.get_method_display()  # 返回choices的display name而不是实际值


class StepSerializer(serializers.ModelSerializer):
    '''
    注意不会自动帮你创建，自己手动实现
    会把Step对象的request字段用RequestSerializer序列化成完整的嵌套数据，返回给前端
    '''
    request = RequestSerializer()
    '''
    写成PrimaryKeyRelatedField就可以避免传过来的参数是数字，非要求是Case实体了
    '''
    # belong_case = serializers.PrimaryKeyRelatedField(
    #     queryset=Case.objects.all(), required=False
    # )
    belong_case_id = serializers.IntegerField(required=False)

    def validate(self, attrs):
        template = {
            'variables': dict,
            'request': dict,
            'extract': dict,
            'validate': list,
            'setup_hooks': list,
            'teardown_hooks': list,
        }
        Utils.validate(attrs, template)
        return attrs

    class Meta:
        model = Step
        fields = '__all__'

    def create(self, validated_data):
        req = validated_data.pop('request')
        step_obj = Step.objects.create(**validated_data)
        req['step_id'] = step_obj.id
        request_serializer = RequestSerializer(data=req)
        '''
        raise_exception=True表示不用写else抛出异常了，自动写了相当于
        '''
        if request_serializer.is_valid(raise_exception=True):
            request_serializer.save()
        # 构造步骤
        return step_obj


class ConfigSerializer(serializers.ModelSerializer):
    project = ProjectSerializer(read_only=True)

    def validate(self, attrs):
        template = {
            'variables': dict,
            'parameters': dict,
            'export': list
        }
        Utils.validate(attrs, template)
        return attrs

    class Meta:
        model = Config
        fields = '__all__'


class CaseSerializer(serializers.ModelSerializer):
    '''
    序列化里面config里面的字段，会把嵌套的 config 字段序列化成字典，那不加这个，就会直接返回数据库里原始的 JSON 数据？
    '''
    config = ConfigSerializer()
    # config_id = serializers.PrimaryKeyRelatedField(
    #     queryset=Config.objects.all(), write_only=True, source='config'
    # )
    '''
    queryset 你可以从哪个模型里查找传入的 ID
    write_only 只在请求（反序列化）中使用,read_only=True只看序列化，传参不要
    source 你前端传来的 project_id，其实是要赋值给模型中的 project 字段（外键
    serializers.PrimaryKeyRelatedField会把你传进来的id查找到模型直接变成模型
    '''
    project_id = serializers.IntegerField(write_only=True, required=False)
    '''
    注意，要显示该字段记得要有反向查询的字段，否则找不到
    '''
    teststeps = StepSerializer(many=True, required=False)
    create_by = UserSerializer(write_only=True, required=False)
    update_by = UserSerializer(write_only=True, required=False)

    def validate(self, attrs):
        template = {
            'config': dict,
            'teststeps': list,
        }
        Utils.validate(attrs, template)
        return attrs

    class Meta:
        model = Case
        fields = ['config', 'project_id', 'desc', 'file_path', 'update_time', 'id', 'teststeps', 'create_by',
                  'update_by', 'create_time', 'file_path']

    # 覆盖父类新增方法
    def create(self, validated_data):
        '''
        :param validated_data: 校验后的入参 --字典格式
        :return:
        '''
        config_kws = validated_data.pop('config')  # 提取出config参数
        project = Project.objects.get(pk=validated_data.pop('project_id'))
        config = Config.objects.create(project=project, **config_kws)
        # 创建用例
        file_path = f'{project.name}_{config.name}.json'  # 项目名+配置名.json

        steps = []
        if "teststeps" in validated_data:
            steps = validated_data.pop("teststeps", [])
        case = Case.objects.create(file_path=file_path, config=config, **validated_data)

        if steps:
            for step in steps:
                step['belong_case_id'] = case.id
                serializer = StepSerializer(data=step)
                if serializer.is_valid(raise_exception=True):
                    serializer.save()

        return case

    def update(self, instance, validated_data):
        '''
        :param instance: 当前要修改的对象模型
        :param validated_data:
        :return:
        '''
        config_kws = validated_data.pop('config')  # 提取出config参数
        project = Project.objects.get(pk=validated_data.pop('project_id'))
        config_kws['project'] = project.id
        conf_serializer = ConfigSerializer(instance=instance.config, data=config_kws)
        if conf_serializer.is_valid():
            conf_serializer.save()
        else:
            raise ValidationError(conf_serializer.errors)

        steps = instance.teststeps.all()
        for step in steps:
            step.delete()
        teststeps = validated_data.pop('teststeps')
        for step in teststeps:
            step['belong_case_id'] = instance.id
            step_serializer = StepSerializer(data=step)
            if step_serializer.is_valid():
                step_serializer.save()
            else:
                raise ValidationError(step_serializer.errors)
        # 利用python反射机制自动赋值
        for k, v in validated_data.items():
            setattr(instance, k, v)
        instance.save()
        return instance

    def to_json_file(self, path=None):
        if path is None:
            path = self.instance.file_path
        if not path.endswith('.json'):
            path += '.json'

        path = f"{settings.BASE_DIR}/testfile/{path}"
        print("path:", path)
        # 生成json文件,把它转成 JSON 格式的 字节流（bytes）。
        # content = JSONRenderer().render(self.data, accepted_media_type='application/json; indent=4')
        # print("content:",content)
        # with open(path,'wb') as file:
        #     file.write(content)
        validate = Utils.filter_data(self.data)
        with open(path, 'w', encoding='utf-8') as f:
            # indent 后，生成的 JSON 会是多行格式，每层结构都会自动缩进传的空格数
            json.dump(validate, f, indent=4)
        return path
